


iMacros.conv_recorder = (function() {
    "use strict";
    const Cu = Components.utils;
    const Ci = Components.interfaces;
    const Cc = Components.classes;

    function require(url) {
        let scope = {};
        Cu.import(url, scope);
        return scope;
    }

    let {MiscUtils, StrUtils} = require("chrome://jsm/content/utils.js");
    let {Rijndael} = require("chrome://jsm/content/rijndael.js");
    let {Prefs} = require("chrome://jsm/content/Preferences.js");
    let {pm} = require("chrome://jsm/content/passwordManager.js");
    let {Dialogs} = require("chrome://jsm/content/Dialogs.js");
    let {FIO} = require("chrome://jsm/content/FileHelpers.js");
    let {Downloads} = require("resource://gre/modules/Downloads.jsm");
    let {Services} = require("resource://gre/modules/Services.jsm");
    let {Storage} = require("chrome://jsm/content/Storage.js");

    
    function MacroRecorder() {
        
        this.versionId = "VERSION BUILD="+imacros_version+" RECORDER=FX";
        this._onBrowserUIClick = this.onBrowserUIClick.bind(this);

        
        
        this._onTabSelect = this.onTabSelect.bind(this);
        this._onTabClose = this.onTabClose.bind(this);
	this._onFrameMessage = this.onFrameMessage.bind(this);
	
	const recorder_url = "chrome://imacros/content/frame_recorder.js";
	let mm = window.messageManager;
	mm.loadFrameScript(recorder_url, true);
	mm.addMessageListener("imacros:record-action", this._onFrameMessage);
    }


    const pwd_re = new RegExp("^(.+\\btype=input:password\\b.+content=)"+
                              "(\\S+)\\s*$", "i");

    MacroRecorder.prototype.onFrameMessage = function(msg) {
        
	if (this.paused)
	    return;
	if (msg.name == "imacros:record-action") {
            let m = pwd_re.exec(msg.data.action);
            
            if (m != null) {
                let head = m[1], plaintext = m[2];
                this.onPasswordElement(head, plaintext);
            } else if (msg.data.extra) {
                this.packAction(msg.data.action, msg.data.extra);
            } else {
                this.recordAction(msg.data.action);
            }
	}
    };

    
    MacroRecorder.prototype.attachBrowser = function(browser) {
	this.currentBrowser = browser;
	let mm = this.currentBrowser.messageManager;
	mm.sendAsyncMessage("imacros:start-recording", {
            favorId: this.favorId, recordMode: this.recordMode
        });
    };

    MacroRecorder.prototype.detachBrowser = function() {
	try {
	    let mm = this.currentBrowser.messageManager;
	    mm.sendAsyncMessage("imacros:stop-recording", {});
	} catch (e) {
	    
	    Cu.reportError(e);
	} finally {
	    this.currentBrowser = null;
	}
    };

    MacroRecorder.prototype.askPassword = function() {
        this.showPwdDialog = false;
        const conf_msg = "Would you like to encrypt your password?";
        let conf = Services.prompt.confirm(window, "iMacros", conf_msg);
        if (!conf)
            return {key: "", type: pm.TYPE_NONE};
        const pwd_type_msg = "Would you like to use temporary "+
            "encryption key? It is only remembered while iMacros is active. "+
            "Master key will be used otherwise.";
        let use_tmp = 0 == Services.prompt.confirmEx(
            window, "iMacros", pwd_type_msg,
            Services.prompt.STD_YES_NO_BUTTONS, 
            null, null, null, null, {}
        );
        let key = use_tmp ? pm.getSessionPwd() : pm.getMasterPwd();
        if (!key) {
            const enter_master = "Enter master key (it will be stored"+
                  " in Firefox password manager and available across sessions)";
            const enter_temp = "Enter temporary key (it will be available as "+
                  "long as iMacros is active)";
            let password = {value: null};
            let ok = Services.prompt.promptPassword(
                window, "iMacros", 
                use_tmp ? enter_temp : enter_master,
                password, null, {}
            );
            if (!ok) 
                return this.askPassword();
            key = password.value;
            pm[use_tmp ? "setSessionPwd" : "setMasterPwd"](key);
        }
        
        return this.encryptionParameters = 
            {key: key, type: use_tmp ? pm.TYPE_TEMP : pm.TYPE_STORED};
    };


    MacroRecorder.prototype.onPasswordElement = function(head, plaintext) {
        let cyphertext = plaintext;
        let enc = this.showPwdDialog ? this.askPassword() :
            this.encryptionParameters;
        let enc_type = "SET !ENCRYPTION ";
        switch(enc.type) {
        case pm.TYPE_NONE:
            enc_type += "NO"; break;
        case pm.TYPE_STORED:
            enc_type += "STOREDKEY";
            cyphertext = Rijndael.encryptString(plaintext, enc.key);
            break;
        case pm.TYPE_TEMP:
            enc_type += "TMPKEY";
            cyphertext = Rijndael.encryptString(plaintext, enc.key);
            break;
        }
        
        let p1 = this.actions.pop();
        let p2 = this.actions.pop();
        if (p1.startsWith("TAG") && p2.startsWith("SET !ENCRYPTION")) {
            iMacros.panel.removeLastLine();
            iMacros.panel.removeLastLine();
        } else {
            this.actions.push(p2);
            this.actions.push(p1);
        }
        this.recordAction(enc_type);
        this.recordAction(head+cyphertext);
    };


    
    MacroRecorder.prototype.beforeRecordAction = function(rec) {
        
	let match_part = rec;
	if (/^(tag .*\s+content\s*=)/i.test(rec))
            match_part = RegExp.$1;
	if (this.actions.length &&
            this.actions[this.actions.length-1].indexOf(match_part) == 0) {
            
            this.actions.pop();
            iMacros.panel.removeLastLine();
	}
    };

    MacroRecorder.prototype.afterRecordAction = function(rec) {
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    };

    MacroRecorder.prototype.recordAction = function(rec) {
        this.beforeRecordAction(rec);
        this.actions.push(rec);
        iMacros.panel.addLine(rec);
        this.afterRecordAction(rec);
    };

    MacroRecorder.prototype.packClickEvent = function(rec, extra) {
        console.assert(this.actions.length >= 2, "click event should be "+
                       "preceeded by at least two actions");
        let mdown_action = "EVENT TYPE=MOUSEDOWN SELECTOR=\""+
            extra.selector+"\"";
        let mup_action = "EVENT TYPE=MOUSEUP";
        if (this.actions[this.actions.length-2].indexOf(mdown_action) == 0 &&
            this.actions[this.actions.length-1].indexOf(mup_action) == 0) {
            this.actions.pop();
            this.actions.pop();
            iMacros.panel.removeLastLine();
            iMacros.panel.removeLastLine();
            this.recordAction(rec);
        }
    };

    MacroRecorder.prototype.packDblClickEvent = function(rec, extra) {
        console.assert(this.actions.length >= 2, "click event should be "+
                       "preceeded by at least two actions");
        let click_action = "EVENT TYPE=CLICK SELECTOR=\""+extra.selector+"\"";
        if (this.actions[this.actions.length-2].indexOf(click_action) == 0 &&
            this.actions[this.actions.length-1].indexOf(click_action) == 0) {
            this.actions.pop();
            this.actions.pop();
            iMacros.panel.removeLastLine();
            iMacros.panel.removeLastLine();
            this.recordAction(rec);
        }
    };

    MacroRecorder.prototype.packMouseMoveEvent = function(rec, extra) {
        const re = new RegExp('^events? type=mousemove\\b.+'+
                              '\\points?="(\\S+)"', "i");
        if (this.actions.length && this.prevTarget == extra.selector) {
            let prev_action = this.actions[this.actions.length-1];
            let m = re.exec(prev_action);
            if ( m ) {
                
                this.actions.pop();
                iMacros.panel.removeLastLine();
                
                
                
                
                
                
                this.recordAction(
                    "EVENTS TYPE=MOUSEMOVE SELECTOR=\""+extra.selector+"\""+
                        " POINTS=\""+m[1].toString()+
                        ",("+extra.point.x+","+extra.point.y+")\""+
                        (extra.modifiers ?
                                " MODIFIERS=\""+extra.modifiers+"\"" : "")
                );
            }
        } else {
            this.prevTarget = extra.selector;
            this.recordAction(rec);
        }
    };

    MacroRecorder.prototype.packKeyPressEvent = function(rec, extra) {
        let enc = this.encryptionParameters, encrypt = false;
        if (extra.encrypt && extra.use_char) {
            if (this.showPwdDialog) {
                enc = this.askPassword();
                let xx = {};
                xx[pm.TYPE_NONE] = "NO";
                xx[pm.TYPE_STORED] = "STOREDKEY";
                xx[pm.TYPE_TEMP] = "TMPKEY";
                let enc_type = "SET !ENCRYPTION "+xx[enc.type];
                this.recordAction(enc_type);
            }
            encrypt = enc.type != pm.TYPE_NONE;
        }
        
        const strre = "\"(?:[^\"\\\\]|\\\\[0btnvfr\"\'\\\\])*\"";
        const re = new RegExp('^events? type=keypress\\b.+'+
                              '\\b(key|char)s?=(\\d+|'+strre+')'+
                              '(?:modifiers="(\\S+)")?', "i");

        if (this.actions.length && this.prevTarget == extra.selector) {
            let prev_action = this.actions[this.actions.length-1];
            let m = re.exec(prev_action);
            let matches = m &&
                (extra.use_char ? m[1] == "CHAR" :
                 (m[1] == "KEY" &&
                  (extra.modifiers ? m[3] == extra.modifiers : !m[3]))
                );
            
            if (matches) {
                
                this.actions.pop();
                iMacros.panel.removeLastLine();
                let chars = StrUtils.unwrap(m[2]), keys = [];
                if (!extra.use_char) { 
                    if (/^\d+$/.test(chars))
                        keys = [parseInt(chars)];
                    else if (/^\[/.test(chars))
                        keys = JSON.parse(chars);
                    keys.push(extra.key);
                } else if (encrypt ) {
                    
                    try {
                        chars = Rijndael.decryptString(chars, enc.key);
                    } catch (e) {
                        
                        
                        this.stop();
                        iMacros.panel.showErrorMessage(
                            "Encryption type or stored password was changed"+
                                " while recording!"
                        );
                        return;
                    }
                    chars = Rijndael.encryptString(chars + extra.char, enc.key);
                } else if (extra.use_char) {
                    chars += extra.char;
                }

                this.recordAction(
                    "EVENTS TYPE=KEYPRESS SELECTOR=\""+extra.selector+"\""+
                        (extra.use_char?
                         " CHARS=\""+StrUtils.escapeLine(chars)+"\"" :
                         (" KEYS=\""+JSON.stringify(keys)+"\""+
                          (extra.modifiers.length ?
                           " MODIFIERS=\""+extra.modifiers+"\"" : "")
                         )
                        )
                );
                return;
            }
        }
        
        this.prevTarget = extra.selector;
        if (encrypt) {
            
            let char = Rijndael.encryptString(extra.char, enc.key);
            this.recordAction(
                "EVENT TYPE=KEYPRESS SELECTOR=\""+extra.selector+"\""+
                    " CHAR=\""+StrUtils.escapeLine(char)+"\""
            );
        } else {
            this.recordAction(rec);
        }
    };

    MacroRecorder.prototype.packAction = function(rec, extra) {
        
        if (extra.pack_type == "click") {
            this.packClickEvent(rec, extra);
        } else if (extra.pack_type == "dblclick") {
            this.packDblClickEvent(rec, extra);
        } else if (extra.pack_type == "mousemove") {
            this.packMouseMoveEvent(rec, extra);
        } else if (extra.pack_type == "keypress") {
            this.packKeyPressEvent(rec, extra);
        }
    };

    
    MacroRecorder.prototype.start = function() {
        
        let branch = Services.prefs.getBranch("browser.link.");
        let open_newwindow = branch.getIntPref("open_newwindow");
        let open_newwindow_restriction = branch.
            getIntPref("open_newwindow.restriction");
        Storage.set("open_newwindow", open_newwindow);
        if (open_newwindow != 3) {
            branch.setIntPref("open_newwindow", 3);
        }
        Storage.set("open_newwindow_restriction", open_newwindow_restriction);
        if (open_newwindow_restriction != 0) {
            branch.setIntPref("open_newwindow.restriction", 0);
        }

        
        iMacros.panel.clearAllLines();
        
        iMacros.panel.mboxClearMessage();
        
        this.registerObservers();
        this.hookEvents();
	this.attachBrowser(gBrowser.selectedBrowser);
        this.downloadDlgHandled = false;
        
        this.rclickEvent = null;
        
        this.curTabIndex = gBrowser.mTabContainer.selectedIndex;
        this.prevTab = 0;
        
        this.likelyUITabClose = false;

        
        this.actions = new Array();

        
        this.recordPageTitle = gBrowser.selectedBrowser.contentTitle;

        
        this.showPwdDialog = true;
        this.encryptionParameters = {key: "", type: pm.TYPE_NONE};

        let conf_promise = Promise.resolve(false);
        let ask = Prefs.showTabCloseDialog;
        if (ask && gBrowser.tabContainer.childNodes.length > 1) {
            let check = {value: true};
            let msg = "Would you like to close all tabs before recording?";
            msg += "\n\n";
            msg += "If you select YES, iMacros will close all other open tabs"+
                "\n and add the \"TAB CLOSEALLOTHER\" command to the macro.";
            let chck_msg = "Show this dialog next time";
            conf_promise = Dialogs.confirmCheck("iMacros", msg, chck_msg, check)
                .then(yes => {
                    Prefs.showTabCloseDialog = check.value;
                    if (yes) {
                        gBrowser.removeAllTabsBut(gBrowser.selectedTab);
                        return true;
                    }
                    return false;
                });
        }

        this.recording = true;
        this.notifyStateChange("recording");
        iMacros.panel.statLine3 = "#Current.iim";
        iMacros.panel.statLine1Status = "recording";
        iMacros.panel.statLine1 = MiscUtils.strings('imacrosrecording');
        
        iMacros.panel.updateControlPanel();
        iMacros.panel.showLines();
        
        
        this.recordAction(this.versionId);
        this.recordAction("TAB T=1");
        conf_promise.then(conf => {
            if (conf)
                this.recordAction("TAB CLOSEALLOTHERS");
            this.recordAction("URL GOTO="+this.currentBrowser.currentURI.spec);
        });
    };

    MacroRecorder.prototype.notifyStateChange = function(state) {
        Services.obs.notifyObservers(window, "imacros-state-change", state);
    };

    MacroRecorder.prototype.stop = function() {
        this.recording = false;
        this.notifyStateChange("idle");
        this.paused = false;
        this.unregisterObservers();
        this.unhookEvents();
	this.detachBrowser(gBrowser.selectedBrowser);
        this.rclickEvent = null;
        
        
        let branch = Services.prefs.getBranch("browser.link.");
        if (Storage.has("open_newwindow")) {
            branch.setIntPref("open_newwindow", Storage.get("open_newwindow"));
            Storage.clear("open_newwindow");
        }

        if (Storage.has("open_newwindow_restriction")) {
            branch.setIntPref(
                "open_newwindow.restriction",
                Storage.get("open_newwindow_restriction")
            );
            Storage.clear("open_newwindow_restriction");
        }

        let name = "#Current.iim";
        let macro = this.getRecordedMacro();
        
        let file_path = FIO.getMacroPath(name);
        return FIO.writeTextFile(file_path, macro).then(() => {
            if (iMacros.panel.sidebarIsOpen) {
                let mtree = iMacros.panel.sidebar.getMTreeObject();
                let item = mtree.findItem(name);
                if (!item) {
                    mtree.insertLeaf(name, mtree.children);
                    item = mtree.findItem(name);
                }
                let idx = mtree.getIndexOfItem(item);
                mtree.tree.view.selection.select(idx);
            }
        }).catch(e => {
            Cu.reportError(e);
            iMacros.panel.showErrorMessage(e);
        });
    };

    
    
    MacroRecorder.prototype.pause =  function() {
        
        if (!this.paused) {
            this.paused = true;
            this.notifyStateChange("paused");
        }

        return Promise.resolve();
    };

    
    MacroRecorder.prototype.unPause =  function() {
        if (this.paused) {
            this.paused = false;
            this.notifyStateChange("recording");
        }
        return Promise.resolve();
    };

    MacroRecorder.prototype.isPaused = function() {
        return this.paused;
    };

    
    MacroRecorder.prototype.observe = function (subject, topic, data) {
        console.log("MacroRecorder.observe topic %s, data %O", topic, data);
        if (subject != window || !this.recording || this.paused)
            return;
        let dlgres = JSON.parse(data);
        
        if (topic == "imacros-commdlg-hook") {
            console.log("imacros-commdlg-hook");
            if (/^(?:alert|confirm|prompt)/.test(dlgres.type)) {
                this.handleJSDialog(dlgres);
            } else if (dlgres.type == "login") {
                console.log("imacros-commdlg-hook, login");
                this.handleLoginDialog(dlgres);
            }
        } else if (topic == "imacros-download-hook") {
            this.handleDownloadDialog(dlgres);
        }
    };

    
    MacroRecorder.prototype.recordDownload =  function(uri, file) {
        if (this.downloadDlgHandled) {
            this.downloadDlgHandled = false;
            return;
        }
        if (uri == gBrowser.selectedBrowser.currentURI.spec) {
	    
            let rec = "SAVEAS TYPE=CPL FOLDER=* "+
                "FILE=+_{{!NOW:yyyymmdd_hhnnss}}";
            this.recordAction(rec);
        } else if (this.rclickEvent) {
            
            this.recordAction(this.rclickEvent.action);
            let lastrec = this.actions.pop();
            iMacros.panel.removeLastLine();
            let rec = "ONDOWNLOAD FOLDER=* "+
                "FILE=+_{{!NOW:yyyymmdd_hhnnss}} WAIT=YES";
            this.recordAction(rec);
            if (this.rclickEvent.tagName == "A") {
                lastrec += " CONTENT=EVENT:SAVETARGETAS";
            } else if (this.rclickEvent.tagName == "IMG") {
                lastrec += " CONTENT=EVENT:SAVEPICTUREAS";
            }
            this.recordAction(lastrec);
            this.rclickEvent = null;
        }
    };
    
    
    MacroRecorder.prototype.handleJSDialog = function (dlgres) {
        let data = "", pos = 1;

        if (dlgres.type == "prompt")
            data = StrUtils.wrap(dlgres.val1);

        if (this.actions.length > 1) {
            let prev_action = this.actions[this.actions.length-2];
            if (/^ondialog pos=(\d+)/i.test(prev_action)) {
                pos = StrUtils.toInteger(RegExp.$1)+1;
            }
        }

        let rec = "ONDIALOG POS="+pos;
        rec += " BUTTON="+(dlgres.accept ? "OK" : "CANCEL");
        rec += " CONTENT="+data;
        let last_rec = this.actions.pop();
        iMacros.panel.removeLastLine();
        this.recordAction(rec);
        this.recordAction(last_rec);
    };


    MacroRecorder.prototype.handleLoginDialog = function(dlgres) {
        if (!dlgres.accept) {
            
            return;
        }
        let user = dlgres.val1, plaintext = dlgres.val2, cyphertext = "";

        let enc = this.showPwdDialog ? this.askPassword() :
            this.encryptionParameters;
        let enc_type = "SET !ENCRYPTION ";
        switch(enc.type) {
        case pm.TYPE_NONE:
            enc_type += "NO";
            cyphertext = plaintext;
            break;
        case pm.TYPE_STORED:
            enc_type += "STOREDKEY";
            cyphertext = Rijndael.encryptString(plaintext, enc.key);
            break;
        case pm.TYPE_TEMP:
            enc_type += "TMPKEY";
            cyphertext = Rijndael.encryptString(plaintext, enc.key);
            break;
        }
        
        let rec = "ONLOGIN USER="+user+" PASSWORD="+cyphertext;
        let lastrec = this.actions.pop();
        iMacros.panel.removeLastLine();
        this.recordAction(enc_type);
        this.recordAction(rec);
        this.recordAction(lastrec);
    };


    MacroRecorder.prototype.handleDownloadDialog = function() {
        let rec = "ONDOWNLOAD FOLDER=*"+
            " FILE=+_{{!NOW:yyyymmdd_hhnnss}}"+
            " WAIT=YES";
        let sliced_actions = new Array(), prev_action = "";
        
        
        do {
            prev_action = this.actions.pop();
            sliced_actions.push(prev_action);
            iMacros.panel.removeLastLine();
        } while(this.actions.length && !/^(?:TAG|URL)/.test(prev_action));
        this.recordAction(rec);
        sliced_actions.reverse().forEach(x => this.recordAction(x));
        this.downloadDlgHandled = true;
    };


    
    
    MacroRecorder.prototype.onOpenWindow = function(xulwin) {
        
        let url = "chrome://mozapps/content/downloads/unknownContentType.xul";
        
        let win = xulwin.QueryInterface(Ci.nsIInterfaceRequestor)
            .getInterface(Ci.nsIDOMWindow);
        
        win.addEventListener("load", function listener() {
            win.removeEventListener("load", listener, false);
            if (win.document.URL == url) {
                Services.scriptloader.loadSubScript(
                    "chrome://imacros/content/downloadHook.js", win
                );
                
            }
        });
    };

    MacroRecorder.prototype.onCloseWindow = function(xulwin) {};
    MacroRecorder.prototype.onWindowTitleChange = function(xulwin, title) {};

    MacroRecorder.prototype.registerObservers = function() {
        Downloads.getList(Downloads.PUBLIC)
            .then(l => l.addView(this))
            .then(() => {
                Services.obs.addObserver(this, "imacros-commdlg-hook", false);
                Services.wm.addListener(this);
            }).catch(Cu.reportError);
    };

    MacroRecorder.prototype.unregisterObservers = function() {
        Downloads.getList(Downloads.PUBLIC)
            .then(l => l.removeView(this))
            .then(() => {
                Services.obs.removeObserver(
                    this, "imacros-commdlg-hook", false
                );
                Services.wm.removeListener(this);
            }).catch(Cu.reportError);
    };


    MacroRecorder.prototype.onDownloadAdded = function(dl) {
        if (dl.succeeded || dl.stopped)
            return;
        if (this.recording && !this.paused) {
            this.recordDownload(dl.source.url, dl.target.path);    
        }
    };
    
    MacroRecorder.prototype.onDownloadChanged = function(dl) {
        
    };

    MacroRecorder.prototype.onDownloadRemoved = function(dl) {
        
    };


    
    MacroRecorder.prototype.addWaitCommand = function () {
        let inout = {confirm: false, period: 5};
        window.openDialog('chrome://imacros/content/wait.xul','',
                          'modal,centerscreen', inout);
        if (!inout.confirm)
            return;
        let rec = "WAIT SECONDS="+inout.period;
        this.recordAction(rec);
    };


    
    MacroRecorder.prototype.takeScreenshot = function() {
        let rec = "SAVEAS TYPE=PNG FOLDER=* FILE=*";
        this.recordAction(rec);
    };


    MacroRecorder.prototype.savePageAs = function() {
        let rec = "SAVEAS TYPE=HTM FOLDER=* FILE=*";
        this.recordAction(rec);
    };

    
    
    MacroRecorder.prototype.clearCookies = function() {
        let rec = "CLEAR";
        this.recordAction(rec);
    };


    
    MacroRecorder.prototype.getRecordedMacro = function() {
        if (!this.actions)
            return "";
        return this.actions.join("\n");
    };

    MacroRecorder.prototype.onTabSelect = function(event) {
        if (!this.recording || this.paused)
            return;
        let current = gBrowser.tabContainer.selectedIndex;
        if ((current = current - this.curTabIndex) < 0) {
            iMacros.panel.showInfoMessage(
                "Note: Tabs LEFT "+
                    "of the start tab are not recorded."
            );
            return;
        }
        if (current == this.prevTab)
            return;
        
        this.prevTab = current;
        
        this.recordAction("TAB T=" + (current + 1));
	this.detachBrowser();
	this.attachBrowser(gBrowser.selectedBrowser);
    };


    MacroRecorder.prototype.onNewNavigatorTab = function() {
        if (!this.recording || this.paused)
            return;
        this.recordAction("TAB OPEN");
    };

    MacroRecorder.prototype.onBrowserUIClick = function(event) {
	if (!this.recording || this.paused)
            return;
	if (!/^chrome:\/\//.test(event.target.baseURI))
	    return;
        
        
        
        
        
        
        

        
        
        if (event.target.tagName == "tab" &&
            event.rangeParent.tagName == "xul:hbox" &&
            event.originalTarget.tagName == "xul:toolbarbutton")
            this.likelyUITabClose = true;
    };

    MacroRecorder.prototype.on_cmd_close = function () {
        if (!this.recording || this.paused)
            return;
        this.likelyUITabClose = true;
    };

    MacroRecorder.prototype.onTabClose = function (event) {
        if (!this.recording || this.paused)
            return;

        if (!this.likelyUITabClose)
            return;       
        this.likelyUITabClose = false;

        
        let tab = event.target;
        if (!tab.selected) {
            let tabs = getBrowser().tabContainer.childNodes;
            let n = 0;
            while (n < tabs.length && tab != tabs[n])
                n++;
            n = n - this.curTabIndex + 1;
            this.recordAction("TAB T="+n);
        }
        
        this.recordAction("TAB CLOSE");
        
    };


    MacroRecorder.prototype.hookEvents = function() {
        
        let con = gBrowser.tabContainer;
        con.addEventListener("TabSelect", this._onTabSelect, false);
        con.addEventListener("TabClose", this._onTabClose, false);
        window.addEventListener("click", this._onBrowserUIClick, true);
        

        
        
        
        gURLBar.imacros_hook_handleCommand = gURLBar.handleCommand;
        gURLBar.handleCommand = function(param) {
            iMacros.recorder.onNavigate();
            return gURLBar.imacros_hook_handleCommand(param);
        };
        let cmd = document.getElementById('cmd_newNavigatorTab');
        this.oncommand_cmd_newNavigatorTab = cmd.getAttribute("oncommand");
        cmd.setAttribute("oncommand", "iMacros.recorder.onNewNavigatorTab();"+
                         this.oncommand_cmd_newNavigatorTab);
        
        
        
        cmd = document.getElementById('cmd_close');
        this.oncommand_cmd_close = cmd.getAttribute("oncommand");
        cmd.setAttribute("oncommand", "iMacros.recorder.on_cmd_close();"+
                         this.oncommand_cmd_close);
    };


    MacroRecorder.prototype.unhookEvents = function() {
        
        let con = gBrowser.tabContainer;
        con.removeEventListener("TabSelect", this._onTabSelect, false);
        con.removeEventListener("TabClose", this._onTabClose, false);
        window.removeEventListener("click", this._onBrowserUIClick, true);
        

        gURLBar.handleCommand = gURLBar.imacros_hook_handleCommand;
        gURLBar.imacros_hook_handleCommand = null;
        let cmd = document.getElementById('cmd_newNavigatorTab');
        cmd.setAttribute("oncommand", this.oncommand_cmd_newNavigatorTab);
        this.oncommand_cmd_newNavigatorTab = null;
        cmd = document.getElementById('cmd_close');
        cmd.setAttribute("oncommand", this.oncommand_cmd_close);
        this.oncommand_cmd_close = null;
    };


    Object.defineProperty(MacroRecorder.prototype, "favorId", {
        get: function() {
            return Prefs.preferIds;
        }
    });

    Object.defineProperty(MacroRecorder.prototype, "useExpertMode", {
        get: function() {
            if (Prefs.recordingMode == "conventional")
                return Prefs.useExpertMode;
            else
                return false;
        }
    });

    Object.defineProperty(MacroRecorder.prototype, "recordMode", {
        get: function() {
            let mode = Prefs.recordingMode;
            if (!mode) {
                mode = "conventional";
                Prefs.recordingMode = mode;
            }
            if (mode == "auto")     
                return "conventional";
            return mode;
        }
    });

     
    MacroRecorder.prototype.onNavigate = function() {
        if (!this.recording || this.paused)
            return;
        let link = gURLBar.value;
        this.recordAction("URL GOTO="+link);
    };

    return new MacroRecorder();

}) ();
